#include "my_sql_parse.h"
#include "package_base.h"
#include "query_pkg.h"
#include "const.h"
#include "log_mgr.h"
#include "utils.h"

extern std::string g_sSharePath;
extern Config g_stConfig;

CSqlParser::CSqlParser()
:m_bInit(false)
,m_pTHD(NULL)
{
}

CSqlParser::~CSqlParser()
{
	if(m_bInit)	End();
}

bool CSqlParser::Init()
{
	if(m_bInit) return m_bInit;
	
	static char* option[3];
	option[0] = "maskd";
	option[1] = "--skip-networking";
	char buff[512];
	snprintf(buff, sizeof(buff), "--basedir=%s", g_sSharePath.c_str());
	option[2] = buff;
	
	static char* group[] = 
	{
		"myparse", (char*)NULL
	};
printf("option:\n%s\n", option[0]);
printf("%s\n", option[1]);
printf("%s\n", option[2]);
	m_bInit=(mysql_server_init(sizeof(option)/sizeof(char*), option, group)==0);
	
	if(m_bInit && !m_pTHD)
	{
		m_pTHD = (THD*)create_embedded_thd(0);
		char* db = "maskd";
		m_pTHD->set_db(db, strlen(db));
	}

	return m_bInit;
}

void CSqlParser::End()
{
	mysql_server_end();
	m_bInit = false;
}

THD* CSqlParser::MyTHD()
{
	return m_pTHD;
}

Result CSqlParser::Parse(Context* pCtx)
{
	assert(m_bInit);
	assert(pCtx);

    Result ret = OK;
	IPkg* pkg = pCtx->pMysql->pSelf;

	m_sSQL.clear();
	
    if(alloc_query(m_pTHD, pkg->Pkg(), pkg->PkgSize()))
    {
      ret = ERR_IGNORE;					// fatal error is set
	  PKG_ERROR("[%s] [%s] [%s], [alloc_query fail]", 
		  retDesc[ret], 
		  pkgCmdInfo[CMD_NUM(pCtx->pMysql->pBuff)].name,
		  pkg->Pkg());
      goto _OUT;
    }
    {
    char* packet_end= m_pTHD->query() + m_pTHD->query_length();
    /* 'b' stands for 'buffer' parameter', special for 'my_snprintf' */
    const char* end_of_stmt= NULL;

	if(!ParseSingle(pCtx, m_pTHD->query(), m_pTHD->query_length(), &end_of_stmt, ret))
	{
		ret = ERR_IGNORE;
		PKG_ERROR("[%s] [%s] [%s], [query parse fail]", 
		retDesc[ret], 
		pkgCmdInfo[CMD_NUM(pCtx->pMysql->pBuff)].name,
		pkg->Pkg());
	}
	//return by call back
	if(ret == ERR_DROP)
	{
		goto _OUT;
	}

    while(!m_pTHD->killed && (end_of_stmt != NULL) && !m_pTHD->is_error())
    {
    	char *beginning_of_next_stmt= (char*)end_of_stmt;

	    /*
        	Multiple queries exits, execute them individually
      		*/
    	close_thread_tables(m_pTHD);
    	ulong length= (ulong)(packet_end - beginning_of_next_stmt);

		/* Remove garbage at start of query */
		while(length > 0 && my_isspace(m_pTHD->charset(), *beginning_of_next_stmt))
      	{
        	beginning_of_next_stmt++;
        	length--;
      	}

      	m_pTHD->set_query(beginning_of_next_stmt, length);
   		m_pTHD->query_id= next_query_id();
	    m_pTHD->set_time(); /* Reset the query start time. */
		if(!ParseSingle(pCtx, beginning_of_next_stmt, length, &end_of_stmt, ret))
		{
			ret = ERR_IGNORE;
			PKG_ERROR("[%s] [%s] [%s], [query parse fail]", 
			retDesc[ret], 
			pkgCmdInfo[CMD_NUM(pCtx->pMysql->pBuff)].name,
			pkg->Pkg());
		}
		//return by call back
		if(ret == ERR_DROP)
		{
			goto _OUT;
		}
    }
    }
_OUT:
	close_thread_tables(m_pTHD);
	m_pTHD->set_query(NULL, 0);
	m_pTHD->command=COM_SLEEP;
	m_pTHD->packet.shrink(m_pTHD->variables.net_buffer_length); // Reclaim some memory
	free_root(m_pTHD->mem_root,MYF(MY_KEEP_PREALLOC));

	return ret;
}

bool CSqlParser::ParseSingle(Context* pCtx, char *rawbuf, uint length, const char** found_semicolon, Result& ret)
{
/*
  Warning.
  The purpose of query_cache_send_result_to_client() is to lookup the
  query in the query cache first, to avoid parsing and executing it.
  So, the natural implementation would be to:
  - first, call query_cache_send_result_to_client,
  - second, if caching failed, initialise the lexical and syntactic parser.
  The problem is that the query cache depends on a clean initialization
  of (among others) lex->safe_to_cache_query and thd->server_status,
  which are reset respectively in
  - lex_start()
  - mysql_reset_thd_for_next_command()
  So, initializing the lexical analyser *before* using the query cache
  is required for the cache to work properly.
  FIXME: cleanup the dependencies in the code to simplify this.
*/
	IPkg* pkg = pCtx->pMysql->pSelf;
	lex_start(m_pTHD);
	mysql_reset_thd_for_next_command(m_pTHD);

  	LEX *lex= m_pTHD->lex;
 	 //lex->select_lex.db = thd->db;
  	Parser_state parser_state;
  	bool err = false;
	
  	if(!(err= parser_state.init(m_pTHD, rawbuf, length)))
  	{
		err= parse_sql(m_pTHD, & parser_state, NULL);
		*found_semicolon= parser_state.m_lip.found_semicolon;
  	}
  	else
		*found_semicolon= NULL;
  
	if(!err)
  	{
		if(!m_pTHD->is_error())
	  	{
			/*
		  Binlog logs a string starting from thd->query and having length
		  thd->query_length; so we set thd->query_length correctly (to not
		  log several statements in one event, when we executed only first).
		  We set it to not see the ';' (otherwise it would get into binlog
		  and Query_log_event::print() would give ';;' output).
		  This also helps display only the current query in SHOW
		  PROCESSLIST.
		  Note that we don't need LOCK_thread_count to modify query_length.
		*/
			if(*found_semicolon && (ulong)(*found_semicolon - m_pTHD->query()))
		  		m_pTHD->set_query_inner(m_pTHD->query(),
								   (uint32)(*found_semicolon -
										 m_pTHD->query() - 1));
			/* Actually execute the query */
			if (*found_semicolon)
			{
		  		lex->safe_to_cache_query= 0;
		  		m_pTHD->server_status |= SERVER_MORE_RESULTS_EXISTS;
			}
			lex->set_trg_event_type_for_tables();
			if(pkg)
			{
				//if(g_stConfig.rule != R_DEFAULT)
				{
					m_sSQL.assign(parser_state.m_lip.yyGetSignBuff(), 
						parser_state.m_lip.yyGetSignBuffPtr()-parser_state.m_lip.yyGetSignBuff());
					CUtils::Replace(CUtils::Replace(m_sSQL, "?, ?", "?"), " . ", ".");
					for (TABLE_LIST *table= lex->query_tables; table; table= table->next_global)
					{
						if(table->alias && table->table_name && strcmp(table->alias, table->table_name)!=0)
						{
							std::string sAlias = std::string(table->alias)+".";
							CUtils::Replace(m_sSQL, sAlias, "");
							sAlias = std::string(table->table_name)+" "+std::string(table->alias);
							CUtils::Replace(m_sSQL, sAlias, std::string(table->table_name));
						}
						if(table->db)
							CUtils::Replace(m_sSQL, std::string(table->db)+".", "");
					}
					CUtils::toLower(CUtils::Replace(m_sSQL, "  ", " "));
				}
 				ret = pkg->Check(pCtx);
			}
	  	}
  	}

  	if (m_pTHD->lex->sphead)
  	{
		delete m_pTHD->lex->sphead;
		m_pTHD->lex->sphead= 0;
  	}
  	lex->unit.cleanup();
  	m_pTHD->end_statement();
  	m_pTHD->cleanup_after_query();

	return !err;

}
